package com.lucas.spring.entity;

import com.lucas.spring.annotation.Autowired;
import com.lucas.spring.annotation.Component;
import com.lucas.spring.annotation.ComponentScan;
import com.lucas.spring.annotation.Scope;
import com.lucas.spring.interfaces.BeanNameAware;
import com.lucas.spring.interfaces.BeanPostProcessor;
import com.lucas.spring.interfaces.InitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author: LiuCheng
 * @description: 容器
 * @date: 2023-11-24 11:02
 **/
public class ApplicationContext {

    private Class configClass;

    /**一级缓存，存放完整的bean对象*/
    private Map<String,Object> singletonMap = new ConcurrentHashMap<>(256);

    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**二级缓存，存放半成品bean对象*/
    private Map<String,Object> secondLevelsingletonMap = new ConcurrentHashMap<>(16);

    /**BeanPostProcessor*/
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public ApplicationContext(Class configClass) throws Exception {
        this.configClass = configClass;

        scan(configClass);
        //创建单例bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")&&!singletonMap.containsKey(beanName)){
                Object bean = createBean(beanName,beanDefinition);
                singletonMap.put(beanName,bean);
            }
        }

    }

    private void scan(Class configClass) throws Exception {
        //解析配置类
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();


        //扫描包路径，找到所有的bean
        ClassLoader classLoader = this.getClass().getClassLoader();
        URL resource = classLoader.getResource(path.replace('.', '/'));
        File file = new File(resource.getFile());

        //关键方法
        findAndSetBeanDefinitions(classLoader, file);
    }

    public Object getBean(String beanName) throws Exception {
        if(beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")){//单例bean从单例map中拿
                Object bean = singletonMap.get(beanName);
                if(bean==null){//说明产生了循环依赖
                    if(!secondLevelsingletonMap.containsKey(beanName)){
                        bean = createBean(beanName,beanDefinition);
                    }else{
                        bean = secondLevelsingletonMap.get(beanName);
                    }
                }
                return bean;
            }else{
                //创建原型bean
                return createBean(beanName,beanDefinition);
            }
        }else{
            throw new NullPointerException("不存在该bean");
        }
    }

    public Object createBean(String beanName,BeanDefinition beanDefinition) throws Exception {
        Class clazz = beanDefinition.getClazz();
        //实例化，属性都为null
        Object instance = clazz.getDeclaredConstructor().newInstance();
        secondLevelsingletonMap.put(beanName,instance);
        //填充属性
        for (Field declaredField : clazz.getDeclaredFields()) {
            if(declaredField.isAnnotationPresent(Autowired.class)){
                Object bean = getBean(declaredField.getName());
                //循环依赖中，在创建第二个对象的时候走这里
                if(secondLevelsingletonMap.containsKey(declaredField.getName())){
                    declaredField.setAccessible(true);
                    declaredField.set(instance,bean);
                    secondLevelsingletonMap.remove(beanName);
                    singletonMap.put(beanName,instance);
                }else{
                    declaredField.setAccessible(true);
                    declaredField.set(instance,bean);
                    secondLevelsingletonMap.remove(beanName);
                }

            }else{
                secondLevelsingletonMap.remove(beanName);
            }
        }

        //调aware接口
        if (instance instanceof BeanNameAware){
            ((BeanNameAware)instance).setBeanName(beanName);
        }

        //init之前的BeanPostProcessor
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
        }

        //init
        if (instance instanceof InitializingBean){
            ((InitializingBean)instance).afterPropertiesSet();
        }

        //init之后的BeanPostProcessor
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
        }

        return instance;
    }

    private void findAndSetBeanDefinitions(ClassLoader classLoader, File file) throws Exception {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File eachFile : files) {
                if (eachFile.isDirectory()) {
                    //是目录就继续找
                    findAndSetBeanDefinitions(classLoader, eachFile);
                } else if (eachFile.isFile() && eachFile.getName().endsWith(".class")) {
                    String fileName = eachFile.getPath();
                    fileName = fileName.substring(fileName.indexOf("classes") + 8, fileName.indexOf(".class"));
                    Class<?> clazz = classLoader.loadClass(fileName.replace('\\', '.'));
                    if (clazz.isAnnotationPresent(Component.class)) {
                        String beanName;
                        Component component = clazz.getDeclaredAnnotation(Component.class);
                        if (component.value().equals("")) {//如果没有设置名字，设置默认名字为首字母小写
                            String clazzName = clazz.getName();
                            String[] arr = clazzName.split("\\.");
                            char[] charArr = arr[arr.length - 1].toCharArray();
                            charArr[0] += 32;
                            beanName = String.valueOf(charArr);
                        } else {//有名字就用提供的名字
                            beanName = component.value();
                        }

                        //如果是BeanPostProcessor，加入到list中
                        if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(beanPostProcessor);
                        }

                        //创建BeanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition(clazz);
                        //原型bean
                        if (clazz.isAnnotationPresent(Scope.class) &&clazz.getDeclaredAnnotation(Scope.class).value().equals("prototype")) {
                            beanDefinition.setScope(clazz.getDeclaredAnnotation(Scope.class).value());
                        } else {
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                }
            }
        }
    }
}
